﻿namespace Lilium

module Prelude =
    
    /// Used to raise undefined exception
    let inline undefined () = failwith "undefined"

    let inline fst (a, b) = a

    let inline snd (a, b) = b

    let inline constant a b = a

    /// flip :: (a -> b -> c) -> b -> a -> c
    let inline flip f a b = f b a

    /// curry :: ((a, b) -> c) -> a -> b -> c
    let inline curry f a b = f (a, b)

    /// uncurry :: (a -> b -> c) -> (a, b) -> c
    let inline uncurry f (a, b) = f a b 

    /// on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
    let on join map x y = join (map x) (map y)

    /// fixed point combinator
    let rec fix f x = f (fix f) x

    /// Used to pass to function its own result as the lazy value.    
    type private 'a Delayed = { mutable computed : bool; mutable value : 'a }

    let lazyFix f =
        let delayed = { computed = false; value = Unchecked.defaultof<'a> }
        let lazyResult = lazy (if delayed.computed then delayed.value
                                                   else failwith "The delayed value has not been evaluated yet.")
        fun () ->
            delayed.value <- f lazyResult
            delayed.computed <- true
            delayed.value


    // Used to define recursively-typed functions

    type 'a RType = RType of ('a -> 'a RType)

    let appR f a = match f with | RType f -> f a